<!DOCTYPE html>
<html>

<head>
    <title>Bootstrap5 实例</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link href="static/bootstrap.min.css" rel="stylesheet">
    <script src="static/bootstrap.bundle.min.js"></script>
    <script src="static/jquery.min.js"></script>
    <script src="static/three.min.js"></script>
    <style>
        body {
            margin: 0;
            overflow: hidden;
        }

        #three-container {
            width: 100%;
            height: 100%;
        }

        #toolbox {
            position: absolute;
            top: 10px;
            left: 10px;
            background-color: rgba(255, 255, 255, 0.1);
            /* 半透明背景 */
            padding: 10px;
            border-radius: 5px;
            z-index: 1000;
            /* 确保覆盖在Three.js渲染内容之上 */
        }
    </style>
</head>

<body>
    <div id="three-container"></div>
    <div id="toolbox">
        <div class="input-group mb-2">
            <span id="show-time" class="btn btn-info" onclick="hc.show_tools()">0</span>
            <span id="state-message" class="input-group-text"></span>
        </div>
        <div id="tools" style="display:none;">
            <div class="input-group mb-1">
                <button type="button" class="form-control btn btn-primary" onclick="hc.view(this)">前侧</button>
                <button type="button" class="form-control btn btn-primary" onclick="hc.view(this)">后侧</button>
                <button type="button" class="form-control btn btn-primary" onclick="hc.view(this)">左侧</button>
                <button type="button" class="form-control btn btn-primary" onclick="hc.view(this)">右侧</button>
                <button type="button" class="form-control btn btn-primary" onclick="hc.view(this)">俯视图</button>
                <button type="button" class="form-control btn btn-primary" onclick="hc.view(this)">顺时针</button>
                <button type="button" class="form-control btn btn-primary" onclick="hc.view(this)">逆时针</button>
            </div>
            <div class="input-group mb-1">
                <span id="posColorList" class="badge bg-info" style="width: 35px;">0</span>
                <button type="button" class="btn btn-primary" onclick="hc.step(this)">-</button>
                <input class="form-control" type="range" id="myRange" onchange="hc.step(this)" value="0" min="0" max="0"
                    step="1">
                <button type="button" class="btn btn-primary" onclick="hc.step(this)">+</button>
                <button id="man-auto" type="button" class="btn btn-primary" onclick="hc.step(this)">》</button>
            </div>
            <textarea class="form-control" rows="3" id="sgftext" onchange="hc.getSGF(this)"></textarea>
        </div>
    </div>
    <script>
        var health_alert = false;
        function health() {
            const now = new Date();
            const minutes = now.getMinutes();
            if (minutes === 0 || minutes === 30) {
                if (health_alert) {
                    alert("游戏健康忠告：请注意休息，合理安排游戏时间。");
                    health_alert = false;
                }
            } else {
                health_alert = true;
            }
        }

        class SimpleGo { //围棋基本类：下棋，提子
            constructor() {
                this.positions = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s"];
                this.newWeiQi([], {}, []);
                this.goString = { string: [], empty: [] };
            }
            go_handler(item = "B[qd]") {
                var position = item.slice(2, 4);
                if (this.posColor.hasOwnProperty(position)) {
                    return false;
                } else {
                    this.posColor[position] = [item[0]];
                }
                var del_pos = [];
                var near_pos = this.near_positions(position);
                for (var npn in near_pos) {
                    if (this.posColor.hasOwnProperty(near_pos[npn])) {
                        if (this.posColor[near_pos[npn]][0] != this.posColor[position][0]) {
                            this.goString = { 'string': [], 'empty': [] };
                            this.go_string(near_pos[npn]);
                            if (this.goString['empty'].length == 0) {
                                for (var spn in this.goString['string']) {
                                    del_pos.includes(this.goString['string'][spn]) || del_pos.push(this.goString['string'][spn]);
                                }
                            }
                        }
                    }
                }
                if (del_pos.length == 0) {
                    this.goString = { 'string': [], 'empty': [] };
                    this.go_string(position);
                    if (this.goString['empty'].length == 0) {
                        delete this.posColor[position];
                        return false;
                    }
                } else {
                    for (var d_p in del_pos) {
                        delete this.posColor[del_pos[d_p]];
                    }
                }
                this.sgf.push(item);
                this.posColor[position].push(this.sgf.length)
                this.posColorList.push(JSON.parse(JSON.stringify(this.posColor)));
                return true;
            }
            go_string(position) {
                this.goString['string'].push(position);
                var nPos = this.near_positions(position);
                for (var np in nPos) {
                    if (this.posColor.hasOwnProperty(nPos[np])) {
                        if (!this.goString['string'].includes(nPos[np]) && this.posColor[nPos[np]][0] == this.posColor[position][0]) {
                            this.go_string(nPos[np]);
                        }
                    } else {
                        this.goString['empty'].includes(nPos[np]) || this.goString['empty'].push(nPos[np]);
                    }
                }
            }
            near_positions(position) {
                var near_pos = [];
                var row = this.near(position[0]);
                var col = this.near(position[1]);
                for (var r in row) {
                    near_pos.push(row[r] + position[1]);
                }
                for (var c in col) {
                    near_pos.push(position[0] + col[c]);
                }
                return near_pos;
            }
            near(char) {
                switch (char) {
                    case "a":
                        return ["b"];
                    case "s":
                        return ["r"];
                    case "b": case "c": case "d": case "e": case "f": case "g":
                    case "h": case "i": case "j": case "k": case "l": case "m":
                    case "n": case "o": case "p": case "q": case "r":
                    default:
                        var pos = this.positions.indexOf(char);
                        return [this.positions[pos - 1], this.positions[pos + 1]];
                }
            }
            newWeiQi(sgf, posColor, posColorList) {
                this.sgf = sgf;
                this.posColor = posColor;
                this.posColorList = posColorList;
            }
        }

        class ThreeBase {
            constructor(threeContainerId) {
                this.scene = this.create_scene("LightBlue"); //场景
                this.camera = this.create_camera([0, 0, 22]); //相机
                this.renderer = this.create_renderer(); //渲染器
                let threeContainer = document.getElementById(threeContainerId);
                threeContainer.appendChild(this.renderer.domElement);
                this.groupBase = new THREE.Group(); //场景用分组
                this.scene.add(this.groupBase);
                //this.animate_init();
            }
            create_scene(color) {
                let scene = new THREE.Scene();
                scene.background = new THREE.Color(color);
                return scene;
            }
            create_camera(position = [10, 10, 10], origin = [0, 0, 0]) {
                let camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
                camera.position.set(position[0], position[1], position[2]);
                camera.lookAt(new THREE.Vector3(origin[0], origin[1], origin[2]));
                return camera;
            }
            create_renderer() {
                let renderer = new THREE.WebGLRenderer({
                    antialias: true,
                    alpha: true
                });
                renderer.setSize(window.innerWidth, window.innerHeight);
                renderer.shadowMap.enabled = true;
                renderer.shadowMap.type = THREE.PCFSoftShadowMap; // 可选：使用柔和阴影
                renderer.setClearAlpha(0.5);
                return renderer;
            }
            animate() { //动画
                const currentTime = performance.now();
                const elapsedTime = currentTime - this.startTime;
                if (currentTime % 1000 < 100) {
                    if (this.is1000ms) { //每秒执行一次
                        health();
                        $("#show-time").text(Number($("#show-time").text()) + 1);
                        if ($("#state-message").text() != "" && this.is5s < 5) {
                            this.is5s++;
                        } else if ($("#state-message").text() != "") {
                            $("#state-message").text("");
                            this.is5s = 0;
                        }
                        if (this.autoRun) {
                            if (Number(document.getElementById("myRange").value) < this.weiQi.sgf.length - 2) {
                                document.getElementById("myRange").value = Number(document.getElementById("myRange").value) + 1;
                                document.getElementById("posColorList").textContent = document.getElementById("myRange").value;
                                this.show(this.weiQi.posColorList[Number(document.getElementById("myRange").value)]);
                            } else {
                                this.autoRun = false;
                            }
                        }
                    }
                    this.is1000ms = false;
                } else {
                    this.is1000ms = true;
                }
                if (this.isRotation) { //相机顺时针或者逆时针旋转
                    let clockValue = 0
                    if (this.antiClockwise == 1) {
                        clockValue = -0.002;
                    } else if (this.antiClockwise == 2) {
                        clockValue = 0.002;
                    }
                    let radius, theta, phi, x, y, z;
                    [radius, theta, phi] = this.cartesianToSpherical(this.camera.position.x, this.camera.position.y, this.camera.position.z);
                    [x, y, z] = this.sphericalToCartesian(radius, theta, phi + clockValue);
                    this.camera.position.set(x, y, z);
                    this.camera.lookAt(new THREE.Vector3(0, 0, 0));
                }
                //this.groupWeiQi.children[0].material.needsUpdate = true;
                this.renderer.render(this.scene, this.camera); //渲染器刷新
                this.animationFrameId = requestAnimationFrame(this.animate.bind(this)); //按屏幕刷新频率执行动画
            }
            animate_init() { //初始化动画环境，并启动
                this.startTime = null;
                this.animationFrameId = null;
                this.is1000ms = true;
                this.animate_start();
            }
            animate_start() { //动画启动功能
                if (this.animationFrameId == null) {
                    this.startTime = performance.now();
                    this.animate();
                }
            }
            animate_stop() { //动画停止功能
                if (this.animationFrameId !== null) {
                    cancelAnimationFrame(this.animationFrameId);
                    this.animationFrameId = null;
                }
            }
        }
        class ThreeHelper extends ThreeBase {
            constructor(threeContainerId) {
                super(threeContainerId);
                this.groupHelper = new THREE.Group();
                this.scene.add(this.groupHelper);
                /* this.groupHelper.add(this.polar_grid_helper());
                this.groupHelper.add(this.polar_grid_helper());
                this.groupHelper.children[0].rotation.x = Math.PI / 2; */
                this.groupHelper.add(this.ambient_light([0, 0, 0], "White", 5));
            }
            polar_grid_helper(position = [0, 0, 0], color = "Gray") {
                /* 极坐标格辅助对象. 坐标格实际上是2维线数组. */
                const radius = 10;
                const sectors = 16;
                const rings = 8;
                const divisions = 64;

                const helper = new THREE.PolarGridHelper(radius, sectors, rings, divisions);
                helper.position.set(position[0], position[1], position[2]);
                return helper;
            }
            ambient_light(position = [0, 0, 0], color = "White", intensity = 1) {
                /* 创建一个环境光对象。 */
                const light = new THREE.AmbientLight(color, intensity); // 柔和的白光
                light.position.set(position[0], position[1], position[2]);
                return light;
            }
            directional_light(position = [0, 0, 0], color = "White", intensity = 1) {
                /* 平行光（DirectionalLight） */
                const directionalLight = new THREE.DirectionalLight(color, intensity);
                directionalLight.position.set(position[0], position[1], position[2]);
                return directionalLight;
            }
        }
        class ThreeMouse extends ThreeHelper {
            constructor(threeContainerId) {
                super(threeContainerId);
                this.groupMouse = new THREE.Group();
                this.scene.add(this.groupMouse);
                this.setupClickListener(this.renderer.domElement, this); //渲染器canvas的监听事件
            }
            setupClickListener(element, someVariable) {
                element.addEventListener('wheel', function (event) { //鼠标滚轮缩放画布
                    let delta = 0;
                    if (event.type === 'wheel') {
                        delta = -event.deltaY;
                    } else if (event.type === 'mousewheel') {
                        delta = event.wheelDelta;
                    }
                    let radius, theta, phi, x, y, z;
                    [radius, theta, phi] = someVariable.cartesianToSpherical(someVariable.camera.position.x, someVariable.camera.position.y, someVariable.camera.position.z);
                    [x, y, z] = someVariable.sphericalToCartesian(radius + delta / 100, theta, phi);
                    someVariable.camera.position.set(x, y, z);
                    someVariable.camera.lookAt(new THREE.Vector3(0, 0, 0));
                });
                element.addEventListener('mousedown', function (event) { //左键，右键按下初始化功能
                    if (event.button == 0) {
                        this.painting = true;
                        someVariable.left_listener(event.clientX, event.clientY);
                    } else if (event.button == 1) {
                        this.paintingM = true;
                        let x = someVariable.camera.position.x;
                        let y = someVariable.camera.position.y;
                        let z = someVariable.camera.position.z;
                        let [radius, theta, phi] = someVariable.cartesianToSpherical(x, z, y);
                        someVariable.basePosition = [event.clientX, event.clientY, radius, theta, phi];
                    }
                });
                element.addEventListener('mousemove', function (event) { //按下左键，右键移动事件
                    if (this.painting) {
                        console.log(event.button, event.clientX, event.clientY);
                    } else if (this.paintingM) {
                        someVariable.camera_rotate(event);
                    }
                });
                element.addEventListener('mouseup', function (event) { //鼠标左键，右键弹起事件
                    if (this.painting) {
                        this.painting = false;
                    } else if (this.paintingM) {
                        this.paintingM = false;
                    }
                });
            }
            cartesianToSpherical(x, y, z) { //笛卡尔坐标转球面坐标
                //r = sqrt(x^2 + y^2 + z^2), θ = arccos(z / r), φ = arctan2(y, x)
                let radius, theta, phi;
                radius = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2) + Math.pow(z, 2));
                theta = Math.acos(z / radius);
                phi = Math.atan2(y, x);
                return [radius, theta, phi];
            }
            sphericalToCartesian(radius, theta, phi) { //球面坐标转笛卡尔坐标
                //x=rsin(θ)cos(ϕ), y=rsin(θ)sin(ϕ), z=rcos(θ)
                let x = radius * Math.sin(theta) * Math.cos(phi);
                let y = radius * Math.sin(theta) * Math.sin(phi);
                let z = radius * Math.cos(theta);
                return [x, y, z];
            }
            camera_rotate(event) { //鼠标中键按住移动时，旋转相机坐标
                const mouseX = event.clientX;
                const mouseY = event.clientY;
                var radius = this.basePosition[2];
                var theta = this.minmax(mouseX - this.basePosition[0], -300, 300) / 300 * Math.PI / 2 + this.basePosition[3];
                var phi = this.minmax(this.basePosition[1] - mouseY, -200, 200) / 200 * Math.PI / 2 + this.basePosition[4];
                let [x, z, y] = this.sphericalToCartesian(radius, phi, theta);
                this.camera.position.set(x, y, z);
                this.camera.lookAt(new THREE.Vector3(0, 0, 0));
            }
            minmax(coordinate, min, max) {
                return Math.min(max, Math.max(min, coordinate));
            }
            left_listener(clientX, clientY) { //左键监听事件，在继承中被覆盖
                let mouse = new THREE.Vector2();
                mouse.x = (clientX / window.innerWidth) * 2 - 1;
                mouse.y = -(clientY / window.innerHeight) * 2 + 1;
                let raycaster = new THREE.Raycaster();
                raycaster.setFromCamera(mouse, this.camera);
                let intersects = raycaster.intersectObjects(this.group1.children, true);
                if (intersects.length > 0) {
                    let intersect = intersects[0];
                    console.log(intersect.point.x, intersect.point.y)
                }
            }
        }

        class ThreeGeometry extends ThreeMouse {
            constructor(threeContainerId) {
                super(threeContainerId);
                this.canvasTexture = null;
                //this.qipan = new CanvasQiPan();
                this.groupGeometry = new THREE.Group();
                this.scene.add(this.groupGeometry);
            }
            create_cube(position = [0, 0, 0], size = [1, 1, 1], color = "Gray") {
                /* 立方缓冲几何体（BoxGeometry）
                BoxGeometry 是四边形的原始几何类，它通常使用构造函数所提供的 “width”、“height”、“depth” 参数来创建立方体或者不规则四边形。 */
                /* BoxGeometry(width : Float, height : Float, depth : Float, widthSegments : Integer, heightSegments : Integer, depthSegments : Integer)
                width — X 轴上面的宽度，默认值为 1。
                height — Y 轴上面的高度，默认值为 1。
                depth — Z 轴上面的深度，默认值为 1。
                widthSegments — （可选）宽度的分段数，默认值是 1。
                heightSegments — （可选）高度的分段数，默认值是 1。
                depthSegments — （可选）深度的分段数，默认值是 1。 */
                const geometry = new THREE.BoxGeometry(size[0], size[1], size[2]);
                const material = new THREE.MeshStandardMaterial({ color: color });
                const cube = new THREE.Mesh(geometry, material);
                cube.position.set(position[0], position[1], position[2]);
                return cube;
            }
            create_plane(position = [0, 0, 0], width = 1, height = 1, color = "Gray") {
                /* 平面缓冲几何体（PlaneGeometry）
                一个用于生成平面几何体的类。 */
                /* width — 平面沿着 X 轴的宽度。默认值是 1。
                height — 平面沿着 Y 轴的高度。默认值是 1。
                widthSegments — （可选）平面的宽度分段数，默认值是 1。
                heightSegments — （可选）平面的高度分段数，默认值是 1。 */
                const geometry = new THREE.PlaneGeometry(width, height);
                const material = new THREE.MeshStandardMaterial({ color: color, side: THREE.DoubleSide });
                material.side = THREE.DoubleSide;
                const plane = new THREE.Mesh(geometry, material);
                plane.position.set(position[0], position[1], position[2]);
                return plane;
            }
            create_torus(position = [0, 0, 0], radius = 1, tube = 0.5, color = "Gray") {
                /* 圆环缓冲几何体（TorusGeometry）
                一个用于生成圆环几何体的类。 */
                /* radius - 环面的半径，从环面的中心到管道横截面的中心。默认值是1。
                tube — 管道的半径，默认值为0.4。
                radialSegments — 管道横截面的分段数，默认值为12。
                tubularSegments — 管道的分段数，默认值为48。
                arc — 圆环的圆心角（单位是弧度），默认值为Math.PI * 2。 */
                const geometry = new THREE.TorusGeometry(radius, tube, 16, 100);
                const material = new THREE.MeshBasicMaterial({ color: color });
                const torus = new THREE.Mesh(geometry, material);
                torus.position.set(position[0], position[1], position[2]);
                return torus;
            }
            create_cylinder(position = [0, 0, 0], color = "Black", radiusTop = 0.2, radiusBottom = 0.5, height = 0.1) { // 创建圆柱体几何体和材质
                var geometry = new THREE.CylinderGeometry(radiusTop, radiusBottom, height, 32); // 参数: 半径顶部, 半径底部, 高度, 径向分段数
                var material = new THREE.MeshStandardMaterial({
                    color: color,
                    transparent: true,
                    opacity: 0.98,
                });
                var cylinder = new THREE.Mesh(geometry, material);
                cylinder.position.set(position[0], position[1], position[2]);
                cylinder.rotation.x = Math.PI / 2;
                return cylinder;
            }
            create_sphere(position = [0, 0, 0], color = "Gray", radius = 1) {
                /* 球缓冲几何体（SphereGeometry）
                一个用于生成球体的类。 */
                /* radius — 球体半径，默认为1。
                widthSegments — 水平分段数（沿着经线分段），最小值为3，默认值为32。
                heightSegments — 垂直分段数（沿着纬线分段），最小值为2，默认值为16。
                phiStart — 指定水平（经线）起始角度，默认值为0。。
                phiLength — 指定水平（经线）扫描角度的大小，默认值为 Math.PI * 2。
                thetaStart — 指定垂直（纬线）起始角度，默认值为0。
                thetaLength — 指定垂直（纬线）扫描角度大小，默认值为 Math.PI。
                该几何体是通过扫描并计算围绕着Y轴（水平扫描）和X轴（垂直扫描）的顶点来创建的。
                因此，不完整的球体（类似球形切片）可以通过为phiStart，
                phiLength，thetaStart和thetaLength设置不同的值来创建，
                以定义我们开始（或结束）计算这些顶点的起点（或终点） */
                const geometry = new THREE.SphereGeometry(radius, 32, 16);
                const material = new THREE.MeshLambertMaterial({ color: color, emissive: color });
                const sphere = new THREE.Mesh(geometry, material);
                sphere.position.set(position[0], position[1], position[2]);
                return sphere;
            }
        }

        class ThreeWeiQi extends ThreeGeometry { //创建棋盘，实现监听棋盘，下棋功能，显示棋盘内容
            constructor(threeContainerId) {
                super(threeContainerId)
                this.weiQi = new SimpleGo();
                this.positions = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s"];
                this.groupQiPan = new THREE.Group();
                this.scene.add(this.groupQiPan);
                this.groupHuaXian = new THREE.Group();
                this.scene.add(this.groupHuaXian);
                this.groupHuaXian.add(this.create_cube([0, 0, -1], [0.2, 0.2, 0.1], "LightGreen"));
                this.groupQi = new THREE.Group();
                this.scene.add(this.groupQi);
                this.groupSphere = new THREE.Group();
                this.scene.add(this.groupSphere);
                this.create_qipan();
                this.current = "B"
                this.currentDict = { "B": "Black", "W": "White", "Black": "B", "White": "W" };
            }
            left_listener(clientX, clientY) { //左键监听事件，棋盘下棋功能
                let mouse = new THREE.Vector2();
                mouse.x = (clientX / window.innerWidth) * 2 - 1;
                mouse.y = -(clientY / window.innerHeight) * 2 + 1;
                let raycaster = new THREE.Raycaster();
                raycaster.setFromCamera(mouse, this.camera);
                let intersects = raycaster.intersectObjects(this.groupQiPan.children, true);
                if (intersects.length > 0) {
                    let intersect = intersects[0];
                    let position = this.coordinate_position(intersect.point.x + 10, 10 - intersect.point.y);
                    let sgf = this.current + "[" + position + "]";
                    if (this.weiQi.go_handler(sgf)) {
                        if (this.current == "B") {
                            this.current = "W";
                        } else {
                            this.current = "B";
                        }
                        let ww = this.weiQi.sgf.length - 1;
                        document.getElementById("myRange").max = ww;
                        document.getElementById("myRange").value = ww;
                        document.getElementById("posColorList").textContent = ww;
                        this.show(this.weiQi.posColor);
                    }
                }
            }
            show(posColor) { //显示棋盘状态
                let x, y, color = { "B": "Black", "W": "White", "Black": "B", "White": "W" };
                while (this.groupQi.children.length > 0) {
                    this.groupQi.remove(this.groupQi.children[0]);
                }
                while (this.groupSphere.children.length > 0) {
                    this.groupSphere.remove(this.groupSphere.children[0]);
                }
                for (let position in posColor) {
                    [x, y] = this.position_coordinate(position);
                    let cylinder = this.create_cylinder([x, y, 0.05], color[posColor[position][0]]);
                    this.groupQi.add(cylinder);
                    let sphere = this.create_sphere([x * 2, y * 2, -5], color[posColor[position][0]]);
                    this.groupSphere.add(sphere);
                }
                let position = this.weiQi.sgf[Number(document.getElementById("myRange").value)].slice(2, 4);
                let [lx, ly] = this.position_coordinate(position);
                this.groupHuaXian.children[0].position.set(lx, ly, 0.1);
            }
            coordinate_position(x, y) { //棋盘three坐标转棋盘位置a～s
                let row, col, grid = 1;
                row = this.minmax(Math.floor((x - grid / 2) / grid), 0, 18);
                col = this.minmax(Math.floor((y - grid / 2) / grid), 0, 18);
                return this.positions[row] + this.positions[col];
            }
            position_coordinate(position) { //棋盘位置a～s转棋盘three坐标
                let row, col, x, y, grid = 1;
                row = this.positions.indexOf(position.slice(0, 1));
                col = this.positions.indexOf(position.slice(1, 2));
                x = Math.floor(row * grid);
                y = Math.floor(col * grid);
                return [x - 9, 9 - y];
            }
            create_qipan() { //创建棋盘
                this.groupQiPan.add(this.create_cube([0, 0, -0.5 - 3e-3], [20, 20, 1], "Yellow")); //棋盘底座
                for (let i = 0; i < 19; i++) { //棋盘19x19线条
                    this.groupHuaXian.add(this.create_plane([i - 9, 0, -1e-3], 0.1, 18, "Black"));
                    this.groupHuaXian.add(this.create_plane([0, i - 9, -1e-3], 18, 0.1, "Black"));
                }
                let xingwei = [-6, 0, 6]; //星位
                xingwei.forEach((row) => {
                    xingwei.forEach((col) => {
                        this.groupHuaXian.add(this.create_torus([row, col, -0.03], 0.3, 0.05, "Black"));
                    });
                });
                this.groupHuaXian.add(this.create_cube([-9.6, 9.6, 0], [0.5, 0.5, 0.01], "LightGreen"));
                //this.groupQiPan.add(this.create_face([0, 0, 0.1], [20, 20], "Black", 0));
            }
        }

        class htmlClick extends ThreeWeiQi {
            constructor(threeContainerId) {
                super(threeContainerId);
                this.animate_init();
                this.animate_start();
                this.combinedPattern = /[\u4e00-\u9fa5a-zA-Z0-9;\[\]\-： ]+/g;
            }
            view(tt) {
                let radius, theta, phi, x, y, z;
                radius = Math.sqrt(Math.pow(this.camera.position.x, 2) + Math.pow(this.camera.position.y, 2) + Math.pow(this.camera.position.z, 2));
                this.camera.up.set(0, 0, 1);
                this.antiClockwise = 0;
                this.isRotation = false;
                switch ($(tt).text()) {
                    case "前侧":
                        phi = Math.PI / 6;
                        theta = Math.PI * 1.5;
                        break;
                    case "后侧":
                        phi = Math.PI / 6;
                        theta = Math.PI * 0.5;
                        break;
                    case "左侧":
                        phi = Math.PI / 6;
                        theta = Math.PI;
                        break;
                    case "右侧":
                        phi = Math.PI / 6;
                        theta = 0;
                        break;
                    case "俯视图":
                        this.camera.up.set(0, 1, 0);
                        phi = 0;
                        theta = 0;
                        break;
                    case "逆时针":
                        this.antiClockwise = 1;
                        this.isRotation = true;
                        return;
                    case "顺时针":
                        this.antiClockwise = 2;
                        this.isRotation = true;
                        return;
                    default:
                        console.log($(tt).val());
                        return;
                }
                [x, y, z] = this.sphericalToCartesian(radius, phi, theta);
                this.camera.position.set(x, y, z);
                this.camera.lookAt(new THREE.Vector3(0, 0, 0));
            }
            step(tt) {
                switch ($(tt).text()) {
                    case "-":
                        document.getElementById("myRange").value = Math.max(0, Number(document.getElementById("myRange").value) - 1);
                        break;
                    case "+":
                        document.getElementById("myRange").value = Math.min(sgo.sgf.length - 1, Number(document.getElementById("myRange").value) + 1);
                        break;
                    case "》":
                        if (this.weiQi.sgf.length == 0) {
                            this.weiQi.newWeiQi([], {}, []);
                            var results = sgftext.match(this.combinedPattern) || [];
                            var format_content = results.join('').split(";");
                            for (var index in format_content) {
                                var item = format_content[index];
                                if (item.length == 5 && this.weiQi.positions.includes(item[2]) && this.weiQi.positions.includes(item[3])) {
                                    this.weiQi.go_handler(item);
                                }
                            }
                            document.getElementById("myRange").max = this.weiQi.sgf.length - 1;
                        }
                        document.getElementById("myRange").value = 0;
                        this.show(this.weiQi.posColorList[0]);
                        setTimeout("hc.autoRun = true", 1000);
                        //if (!this.autoRun) { this.autoRun = true; }
                        break;
                    default:
                        console.log(document.getElementById("myRange").value);
                        break;
                }
                document.getElementById("posColorList").textContent = document.getElementById("myRange").value;
            }
            show_tools() {
                $("#tools").toggle();
                $("#state-message").text("健康忠告：请注意休息，合理安排时间。");
            }
            getSGF(tt) {
                this.weiQi.newWeiQi([], {}, []);
                var results = $(tt).val().match(this.combinedPattern) || [];
                var format_content = results.join('').split(";");
                for (var index in format_content) {
                    var item = format_content[index];
                    if (item.length == 5 && this.weiQi.positions.includes(item[2]) && this.weiQi.positions.includes(item[3])) {
                        this.weiQi.go_handler(item);
                    }
                }
                let ww = this.weiQi.sgf.length - 1;
                document.getElementById("myRange").max = ww;
                document.getElementById("myRange").value = ww;
                document.getElementById("posColorList").textContent = ww;
                this.show(this.weiQi.posColor);
            }
        }
        const hc = new htmlClick("three-container");

        const sgftext = `(;CA[UTF-8]AP[YuanYu]GM[1]FF[4]
SZ[19]
GN[2018腾讯世界人工智能围棋大赛决赛第1局 30日10：00]
DT[2018-07-30]
PB[Golaxy]BR[9d]
PW[FineArt]WR[9d]
KM[7.5]HA[0]RU[Chinese]RE[W+R]TM[3600]TC[10]TT[60]
;B[qd];W[pp];B[dd];W[dp];B[qn];W[qo];B[pn];W[np];B[pj];W[od];B[lc]
;W[me];B[lq];W[qe];B[fq];W[cc];B[qq];W[rn];B[cd];W[dc];B[ed];W[ec]
;B[rm];W[or];B[ro];W[rp];B[qp];W[po];B[sn];W[rq];B[fc];W[fb];B[cn]
;W[er];B[bc];W[bb];B[eb];W[db];B[gb];W[pd];B[eo];W[co];B[bn];W[fp]
;B[ep];W[eq];B[do];W[cq];B[fr];W[iq];B[hp];W[gq];B[gp];W[fo];B[fn]
;W[go];B[hn];W[gr];B[gn];W[fs];B[ei];W[bd];B[be];W[ac];B[cf];W[ih]
;B[qc];W[rf];B[pf];W[qh];B[ob];W[nb];B[oc];W[pc];B[pb];W[rd];B[nd]
;W[qb];B[oe];W[rc];B[nc];W[if];B[hg];W[ig];B[je];W[gi];B[fg];W[hf]
;B[fj];W[lg];B[lf];W[mg];B[kg];W[kh];B[mf];W[ff];B[gg];W[fd];B[gc]
;W[hd];B[fe];W[gd];B[ic];W[gj];B[kf];W[bg];B[cg];W[kj];B[bo];W[gk]
;B[fk];W[id];B[jc];W[eh];B[fh];W[fi];B[ej];W[fl];B[el];W[gl];B[lo]
;W[ae];B[bh];W[em];B[dl];W[km];B[ho];W[qk];B[ol];W[bf];B[ce];W[gf]
;B[ef];W[qj];B[hq];W[hr];B[pi];W[qi];B[lk];W[kk];B[lh];W[li];B[mh]
;W[ng];B[oo];W[pq];B[og];W[nh];B[mi];W[oh];B[qr];W[rr];B[mj];W[ph]
;B[lm];W[kl];B[jh];W[ki];B[bq];W[hh];B[eg];W[ll];B[ml];W[no];B[ln]
;W[nk];B[mk];W[nm];B[nn];W[mm];B[nl];W[mn];B[om];W[mo];B[jq];W[rl]
;B[ql];W[pl];B[qm];W[ir];B[jn];W[pk];B[rk];W[on];B[pm];W[rj];B[sl]
;W[cr];B[jg];W[ji];B[hl];W[il];B[hm];W[jp];B[kp];W[kn];B[ko];W[ik]
;B[cp];W[jr];B[jo];W[hc];B[hb];W[ea];B[af];W[ag];B[fq];W[br];B[ar]
;W[lr];B[mr];W[kq];B[mq];W[ad];B[ah];W[af];B[fm];W[sp];B[nn];W[op]
;B[dq];W[dr];B[jm];W[jl];B[ls];W[on];B[kr];W[nf];B[ne];W[ok];B[hk]
;W[hj];B[pg];W[jd])`

        window.addEventListener('resize', () => {
            hc.camera.aspect = window.innerWidth / window.innerHeight;
            hc.camera.updateProjectionMatrix();
            hc.renderer.setSize(window.innerWidth, window.innerHeight);
        });

    </script>
</body>

</html>